'''
Created on May 10, 2012

@author: bruce


variables:
    1. faster ma: ma_f
    2. slower ma: ma_s
    3. cross over range: cross_range
    4. the least difference of buy signal: buff_b;
    4. the least difference of close signal: buff_c;
    5. threshold volume size: v' 

buy signal:
    1. ma_f crosses over ma_s in the last n1 days;
    2. both ma_f and ma_s are trending upwards;
    3. ma_f - ma_s > buff_b * ma_s;
    4. daily volume > v;
    
close +tive position signal:
    1. ma_f - ma_s <= buff_c * ma_s;
     
'''
from glob import glob
from datetime import date, timedelta
import os
from pprint import pprint

from src.dailystock.indicators.histdata import HistdataBySymbol

class MASimple(object):       
    def __init__(self, verbose = True, csv_dir="input\\", output_dir="output\\"):
        '''
        Constructor
        '''        
        self.__verbose = verbose
        self.__csvdir = csv_dir
        self.__outputdir = output_dir
        self.__currlongs = []
        self.__get_currentlongs()
        self.__symbols = []        
        self.__getsymbols()
        self.__closelongs = []
        self.__addlongs = []
        self.__today = date.today()
        
        # user-defined variables
        self.__2MAs = {'faster' : ('ma', 5), 'slower' : ('ema', 10)}
        self.__backtrackDays = timedelta(days = 100)  
        self.__crossrange = timedelta(days = 5)
        self.__buff_b = 0.001
        self.__buff_c = 0.0005
        self.__minvol = 1000000  
        # the transaction quantity of each stock = transaction fund/stock price
        self.__transact_fund = 3000         
         
    """
    Get the symbols from the data directory.
    """
    def __getsymbols(self):             
        for file_path in glob(os.path.join(self.__csvdir, '*.csv')):   
            filename = os.path.basename(file_path)
            self.__symbols.append(os.path.splitext(filename)[0])
    
    """
    Open and read the current long positions as symbol-quantity tuple.
    """
    def __get_currentlongs(self):
        currlong_file = open(self.__outputdir + "longPos", 'r')
        symbol_quants = currlong_file.readlines()
        for symbol_quant in symbol_quants:
            if not symbol_quant.strip() == "":            
                self.__currlongs.append(tuple(symbol_quant.split())) 
        currlong_file.close()       
    
    """
    Write the current long positions that excludes the ones that are closed
    and adds the ones that just opened to the file.
    """
    def __update_longfile(self):
        currlong_file = open(self.__outputdir + "longPos", 'w')
        for symbol_quant in self.__currlongs + self.__addlongs:
            if symbol_quant not in self.__closelongs:
                currlong_file.write(symbol_quant[0] + "  \t" + str(symbol_quant[1]) + "\n")
        currlong_file.close() 
                   
    """
    Check whether the current stock quote satisfies buying condition.
        1. ma_f crosses over ma_s in the last n1 days;
        2. both ma_f and ma_s are trending upwards;
        3. ma_f - ma_s > buff_b * ma_s;
        4. daily volume > v;
    """ 
    def __buycondition(self, quote_before, quote_after):
        ma_fasterpair = self.__2MAs['faster']        
        ma_faster_bef = vars(quote_before)[ma_fasterpair[0]][ma_fasterpair[1]]
        ma_faster_aft = vars(quote_after)[ma_fasterpair[0]][ma_fasterpair[1]]        
        ma_slowerpair = self.__2MAs['slower']
        ma_slower_bef = vars(quote_before)[ma_slowerpair[0]][ma_slowerpair[1]]
        ma_slower_aft = vars(quote_after)[ma_slowerpair[0]][ma_slowerpair[1]]        
        self.__local_log('add longs: fasterma_bef = ' + str(ma_faster_bef) + ' fasterma_aft = ' + 
                         str(ma_faster_aft) + " slowerma_bef = " + str(ma_slower_bef) + " slowerma_aft = " + str(ma_slower_aft))        
        # return true if moving averages cross over current difference is greater than the buffer.
        return (ma_faster_bef < ma_slower_bef and
                ma_faster_aft - ma_slower_aft > self.__buff_b * ma_slower_aft) #and                  
#                ma_faster_bef < ma_faster_aft and ma_slower_bef < ma_slower_aft and  # both trend upwards
#                quote_after.v > self.__minvol)                                       # volume requirement                                                                                 
                
    """
    Check whether the current long stock quote satisfies closing condition.
    """    
    def __closecondition(self, quoteTdy):
        # check for close position condition ma_f - ma_s <= buff_c * ma_s  
        maFasterPair = self.__2MAs['faster']        
        ma_f = vars(quoteTdy)[maFasterPair[0]][maFasterPair[1]]
        maSlowerPair = self.__2MAs['slower']
        ma_s = vars(quoteTdy)[maSlowerPair[0]][maSlowerPair[1]]        
        self.__local_log('close longs: ma_f = ' + str(ma_f) + " ma_s = " + str(ma_s) + " buff_c = " + str(self.__buff_c))
        self.__local_log(ma_f - ma_s <= self.__buff_c * ma_s)        
        return ma_f - ma_s <= self.__buff_c * ma_s
    
    """
    Get historical raw data and calculate the moving average values.
    """
    def __get_histdata_with2mas(self, symbol):
        # Get the historical data structure with the 10-day ma and 30-day ema.
        symbolHistData = HistdataBySymbol(symbol, self.__csvdir)
        tday = date.today()
        prev_day = tday - self.__backtrackDays            
        symbolHistData.get_histdailyraws(prev_day, tday)
        symbolHistData.calculate_indicators(self.__2MAs.values())        
        return symbolHistData
            
    """
    Close the previous long positions if the stock satisfies the closing condition.
    """
    def __close_prevlongs(self):
        # close the longs that fulfills the close condition.
        self.__local_log( "start of close long position")
        for symbol_quant in self.__currlongs:   
            self.__local_log( "close long check: " + str(symbol_quant[0]))            
            symbolHistData = self.__get_histdata_with2mas(symbol_quant[0])                                   
            quoteTdy = symbolHistData.get_quotebydate(self.__today, False)          
            if self.__closecondition(quoteTdy) == True:
                self.__closelongs.append(symbol_quant)                          
        
        self.__local_log( "close longs: " + str(self.__closelongs))        
    
    """
    Open and add the new long positions if the stock satisfies the buying condition.
    """
    def __appendlongs(self):
        # add to the longs that fulfills the open condition.
        self.__local_log( "start of add long position")
        for symbol in self.__symbols:
            self.__local_log( "start long check: " + str(symbol))
            currlongsymbols = [symbol_quant[0] for symbol_quant in self.__currlongs]
            if symbol not in currlongsymbols:
                symbolHistData = self.__get_histdata_with2mas(symbol)                                   
                quoteTdy = symbolHistData.get_quotebydate(self.__today, False)
                quoteBf = symbolHistData.get_quotebydate(self.__today - self.__crossrange, False)
                if self.__buycondition(quoteBf, quoteTdy) == True:
                    self.__addlongs.append((symbol, int(self.__transact_fund/quoteTdy.c)))
                                        
    
    def __local_log(self, string):
        if self.__verbose:
            pprint(string)
            
    def __selectstocks(self):    
        self.__close_prevlongs()
        self.__appendlongs()
        self.__update_longfile()
        
    def run(self):
        self.__selectstocks() 
        self.__local_log( "add longs: " + str(self.__addlongs))
        self.__local_log( "close longs: " + str(self.__closelongs))  
        return (self.__currlongs, self.__closelongs, self.__addlongs)                       
    
    